package com.wyait.manage.config.security;

import com.wyait.manage.config.security.core.CustomUserDetailsService;
import com.wyait.manage.config.security.encoder.MD5PasswordEncoder;
import com.wyait.manage.config.security.filter.VerifyUserFilter;
import com.wyait.manage.config.security.handler.CustomAccessDeineHandler;
import com.wyait.manage.config.security.handler.CustomAuthenticationEntryPoint;
import com.wyait.manage.config.security.handler.LoginFailureHandler;
import com.wyait.manage.config.security.handler.LoginSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;

import javax.sql.DataSource;

/**
 * 核心配置类
 * 开启方法权限注解支持
 *
 * @author huangjian
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private LoginSuccessHandler successHandler;
    @Autowired
    private LoginFailureHandler failureHandler;
    @Autowired
    private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
    @Autowired
    private CustomAccessDeineHandler customAccessDeineHandler;
    @Autowired
    private VerifyUserFilter verifyUserFilter;
    @Autowired
    private CustomUserDetailsService userDetailsService;
    @Autowired
    private DataSource dataSource;

    /**
     * 返回 RememberMeServices 实例
     *
     * @return the remember me services
     */
    @Bean
    public RememberMeServices rememberMeServices() {
        PersistentTokenBasedRememberMeServices rememberMeServices =
                new PersistentTokenBasedRememberMeServices("INTERNAL_SECRET_KEY", userDetailsService, getPersistentTokenRepository() );
        // 该参数不是必须的，默认值为 "remember-me", 但如果设置必须和页面复选框的 name 一致
        rememberMeServices.setParameter("rememberMe");
        return rememberMeServices;
    }

    /**
     * 记住我的功能
     *
     * @return
     */
    @Bean
    public PersistentTokenRepository getPersistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl = new JdbcTokenRepositoryImpl();
        jdbcTokenRepositoryImpl.setDataSource(dataSource);
        //启动时创建一张表，这个参数到第二次启动时必须注释掉，因为已经创建了一张表
        //jdbcTokenRepositoryImpl.setCreateTableOnStartup(true);
        return jdbcTokenRepositoryImpl;
    }

    /**
     * MD5的方式
     *
     * @return
     */
    @Bean
    public PasswordEncoder md5PasswordEncoder() {
        return new MD5PasswordEncoder();
    }

    /**
     * springSecurity自带的加密方式
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.antMatcher("/**").authorizeRequests();
        // 禁用CSRF 开启跨域
        http.csrf().disable().cors();
        // 允许匿名的url - 可理解为放行接口 - 多个接口使用,分割
        registry.antMatchers(
                "/user/login",
                "/login",
                "/user/login_page").permitAll();

        registry.and()
                .formLogin()
                .loginProcessingUrl("/user/login")
                .successHandler(successHandler)
                .failureHandler(failureHandler);

        registry.and()
                .logout()
                .logoutSuccessUrl("/login");
        registry.antMatchers(HttpMethod.OPTIONS, "/**").denyAll();


        registry
                .and()
                .rememberMe();
        // 其余所有请求都需要认证
        registry.anyRequest().authenticated();
        // 防止iframe 造成跨域
        registry.and().headers().frameOptions().disable();
        //添加自定义异常入口，处理accessdeine异常
        http.exceptionHandling()
                .authenticationEntryPoint(customAuthenticationEntryPoint)
                .accessDeniedHandler(customAccessDeineHandler);

        //自定义登录过滤器
        http.addFilterBefore(verifyUserFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    public void configure(WebSecurity web) {
        // 解决静态资源被拦截的问题（plugins目录在工程resources/static/下）
        web.ignoring().antMatchers("/toLogin",
                "/css/*", "/js/*", "/js/*/*", "/js/*/*/*", "/images/*/**", "/layui/*", "/layui/*/**",
                "/treegrid/*", "/treegrid/*/*", "/fragments/*", "/layout", "/auth/getUserPerms",
                "/user/sendMsg","/token",
                "/toOauth2Login/*");
    }


    public static void main(String[] args) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encodedPassword = passwordEncoder.encode("654321".trim());
        System.out.println(encodedPassword);
    }
}
